home *** CD-ROM | disk | FTP | other *** search
/ Aminet 33 / Aminet 33 - October 1999.iso / Aminet / util / misc / VMM_src.lha / VMM / ffs.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-12-16  |  16.0 KB  |  681 lines

  1. #include <exec/types.h>
  2. #include "defs.h"
  3. #include "ffs.h"
  4.  
  5. static char rcsid [] = "$Id: ffs.c,v 3.6 95/12/16 18:37:01 Martin_Apel Exp $";
  6.  
  7. #define MAX_RETRIES  20
  8. #define WAITING_TIME 5L    /* in ticks, i.e. 1/50 s */
  9. #define MIN(a,b)  (((a)<(b))?(a):(b))
  10.  
  11. #define BLOCKSIZE 512L       /* Only this blocksize supported */
  12. #define BITS_PER_BMBLOCK     ((BLOCKSIZE / 4 - 1) * 32)
  13. #define DO_READ   TRUE
  14. #define DO_WRITE  FALSE
  15.  
  16. PRIVATE ULONG               *Bitmap;
  17. PRIVATE struct FFSRootBlock *RootBlock;
  18. PRIVATE ULONG               *MiscBlock;
  19. PRIVATE ULONG                NumBlocks;
  20. PRIVATE ULONG                DiskType;
  21.  
  22. /*************************************************************************/
  23.  
  24. PRIVATE BOOL MyInhibit (BOOL DoInhibit)
  25.  
  26. {
  27. struct MsgPort *HandlerPort;
  28.  
  29. HandlerPort = (struct MsgPort*)(PagingDevParams.SysTask + 1);
  30. return (BOOL)(DoPkt (HandlerPort, ACTION_INHIBIT, (LONG)DoInhibit, 0L, 0L, 0L, 0L));
  31. }
  32.  
  33. /*************************************************************************/
  34.  
  35. /* The following routine relies on that the paging device has already 
  36.  * been opened and the trap-structs initialized in pageio.c
  37.  */
  38.  
  39. PRIVATE void RWBlock (ULONG block_num, void *buffer, BOOL DoRead)
  40.  
  41. {
  42. ((struct IOStdReq*)TrapInfo[0].IOPacket)->io_Command = DoRead ? CMD_READ : CMD_WRITE;
  43. ((struct IOStdReq*)TrapInfo[0].IOPacket)->io_Flags = 0;
  44. ((struct IOStdReq*)TrapInfo[0].IOPacket)->io_Length = BLOCKSIZE;
  45. ((struct IOStdReq*)TrapInfo[0].IOPacket)->io_Data = buffer;
  46. ((struct IOStdReq*)TrapInfo[0].IOPacket)->io_Offset = 
  47.    (PagingDevParams.low_cyl * PagingDevParams.heads *
  48.     PagingDevParams.secs_per_track + block_num) * BLOCKSIZE;
  49.  
  50. SendIO ((struct IORequest*)TrapInfo[0].IOPacket);
  51. WaitIO ((struct IORequest*)TrapInfo[0].IOPacket);
  52.  
  53. if (((struct IOStdReq*)TrapInfo[0].IOPacket)->io_Error != 0)
  54.   {
  55.   PRINT_DEB ("Error while transferring block %ld", block_num);
  56.   InitError (ERR_FAILED_IO);
  57.   }
  58. }
  59.  
  60. #define ReadBlock(num, buffer)  RWBlock (num, buffer, DO_READ)
  61. #define WriteBlock(num, buffer) RWBlock (num, buffer, DO_WRITE)
  62.  
  63. /*****************************************************************/
  64.  
  65. PRIVATE void Cleanup (void)
  66.  
  67. {
  68. if (Bitmap != NULL)
  69.   FreeVec (Bitmap);
  70. if (RootBlock != NULL)
  71.   FreeMem (RootBlock, BLOCKSIZE);
  72. if (MiscBlock != NULL)
  73.   FreeMem (MiscBlock, BLOCKSIZE);
  74.  
  75. PRINT_DEB ("Uninhibiting partition...", 0L);
  76.  
  77. if (!MyInhibit (FALSE))
  78.   PRINT_DEB ("Could not uninhibit partition", 0L);
  79. else
  80.   PRINT_DEB ("Successfully uninhibited partition", 0L);
  81. }
  82.  
  83. /*****************************************************************/
  84.  
  85. PRIVATE int InitFFS (ULONG BMBlocks)
  86.  
  87. {
  88. int retries = 0;
  89. int rc = SUCCESS;
  90.  
  91. while (!MyInhibit (DOSTRUE) && (retries++ < MAX_RETRIES))
  92.   Delay (WAITING_TIME);
  93.  
  94. if (retries >= MAX_RETRIES)
  95.   return (ERR_INHIBIT_FAILED);
  96.  
  97. PRINT_DEB ("Inhibited partition", 0L);
  98. PRINT_DEB (PartWithColon, 0L);
  99.  
  100. if ((RootBlock = AllocMem (sizeof (struct FFSRootBlock), MEMF_PUBLIC)) == NULL)
  101.   rc = ERR_NOT_ENOUGH_MEM;
  102.  
  103. if ((MiscBlock = AllocMem (BLOCKSIZE, MEMF_PUBLIC)) == NULL)
  104.   rc = ERR_NOT_ENOUGH_MEM;
  105.  
  106. if (BMBlocks != 0L && 
  107.     ((Bitmap = AllocVec (BMBlocks * BLOCKSIZE, MEMF_PUBLIC)) == NULL))
  108.   rc = ERR_NOT_ENOUGH_MEM;
  109.  
  110. if (rc != SUCCESS)
  111.   Cleanup ();
  112.  
  113. return (rc);
  114. }
  115.  
  116. /*****************************************************************/
  117.  
  118. #define MODE_SET 0
  119. #define MODE_CLR 1
  120. #define MODE_TST 2
  121.  
  122. BOOL BitManipulation (ULONG bit_num, UWORD mode)
  123.  
  124. {
  125. ULONG *LW,
  126.       mask;
  127. BOOL was_set;
  128.  
  129. if (bit_num < PagingDevParams.res_start)
  130.   return (FALSE);
  131.  
  132. bit_num -= PagingDevParams.res_start;  /* Reserved blocks are not */
  133.                                        /* in bitmap */
  134. /* The checksum words are still part of the bitmap */
  135. LW = Bitmap + (bit_num / 32) + (bit_num / 32 / (BLOCKSIZE/4 - 1)) + 1;
  136. mask = (1L << (bit_num % 32));
  137.  
  138. was_set = (*LW & mask) != NULL;
  139.  
  140. switch (mode)
  141.   {
  142.   case MODE_SET: *LW |= mask;
  143.                  break;
  144.   case MODE_CLR: *LW &= ~mask;
  145.                  break;
  146.   }
  147.  
  148. return (was_set);
  149. }
  150.  
  151. #define BitSet(i) BitManipulation (i, MODE_SET)
  152. #define BitClr(i) BitManipulation (i, MODE_CLR)
  153. #define BitTst(i) BitManipulation (i, MODE_TST)
  154.  
  155. #define AllocThisBlock(i) BitClr(i)
  156. #define BlockUsed(i)      (!BitTst(i))
  157.  
  158. /*****************************************************************/
  159.  
  160. PRIVATE void Checksum (void *block)
  161.  
  162. {
  163. /* This routine does the checksumming for either a fileheader,
  164.  * a file extension block or the root block. All of them have the 
  165.  * checksum at the same offset.
  166.  */
  167. LONG sum = 0;
  168. ULONG i;
  169. struct FFSFileHeader *fh = (struct FFSFileHeader*) block;
  170.  
  171. fh->Checksum = 0L;
  172.  
  173. for (i = 0; i < BLOCKSIZE / sizeof (ULONG); i++)
  174.   sum += *((ULONG*)fh + i);
  175.  
  176. fh->Checksum = -sum;
  177. }
  178.  
  179. /*****************************************************************/
  180.  
  181. BOOL IsPseudoPart (ULONG header_block, ULONG *first_block, ULONG *last_block)
  182.  
  183. {
  184. struct FFSFileHeader *fh;
  185. struct FFSExtBlock *eb;
  186. int i;
  187. int ErrorCode;
  188.  
  189. if ((ErrorCode = InitFFS (0L)) != SUCCESS)
  190.   {
  191.   InitError (ErrorCode);
  192.   return (FALSE);
  193.   }
  194.  
  195. fh = (struct FFSFileHeader*) MiscBlock;
  196. eb = (struct FFSExtBlock*) MiscBlock;
  197.  
  198. ReadBlock (header_block, fh);
  199.  
  200. #ifdef DEBUG
  201. if (header_block != fh->OwnKey)
  202.   {
  203.   PRINT_DEB ("header_block != fh->OwnKey", 0L);
  204.   ColdReboot ();
  205.   }
  206.  
  207. if (fh->SecondaryType > 0)
  208.   {
  209.   PRINT_DEB ("Directory", 0L);
  210.   ColdReboot ();
  211.   }
  212. #endif
  213.  
  214. *first_block = fh->FirstDataBlock;
  215. *last_block = *first_block - 1;
  216.  
  217. for (;;)
  218.   {  
  219.   for (i = 0; i < eb->BlocksInTable; i++)
  220.     {
  221.     if (eb->DataBlocks [71-i] != *last_block + 1)
  222.       {
  223.       Cleanup ();
  224.       return (FALSE);
  225.       }
  226.     (*last_block)++;
  227.     }
  228.  
  229.   if (eb->ExtBlock == 0)
  230.     break;
  231.  
  232.   ReadBlock (eb->ExtBlock, eb);
  233.   }
  234.  
  235. Cleanup ();
  236. return (TRUE);
  237. }
  238.  
  239. /*****************************************************************/
  240.  
  241. int GetDiskType (char *name, ULONG *DiskType)
  242.  
  243. /* Determines the disk type of a given disk. Used by FFS code and
  244.  * by pageio.c to determine the number of buffers necessary for
  245.  * file paging.
  246.  */
  247. {
  248. int rc;
  249. struct InfoData *InfoData;
  250. BPTR tmp_lock;
  251.  
  252. PRINT_DEB ("Getting information about the following partition", 0L);
  253. PRINT_DEB (name, 0L);
  254.  
  255. if ((InfoData = AllocMem (sizeof (struct InfoData), MEMF_PUBLIC)) != NULL)
  256.   {
  257.   if ((tmp_lock = Lock (name, ACCESS_READ)) != NULL)
  258.     {
  259.     if (Info (tmp_lock, InfoData))
  260.       {
  261.       if (InfoData->id_DiskState != ID_VALIDATED)
  262.         rc = ERR_NOT_VALIDATED;
  263.       else
  264.         {
  265.         *DiskType = InfoData->id_DiskType;
  266.         rc = SUCCESS;
  267.         PRINT_DEB ("NumBlocks (Info) = %ld", InfoData->id_NumBlocks);
  268.         PRINT_DEB ("NumBlocksUsed (Info) = %ld", InfoData->id_NumBlocksUsed);
  269.         }
  270.       }
  271.     else
  272.       {
  273.       PRINT_DEB ("GetDiskType: Couldn't get info for partition", 0L);
  274.       rc = ERR_NO_DISKINFO;
  275.       }
  276.     UnLock (tmp_lock);
  277.     }
  278.   else
  279.     {
  280.     PRINT_DEB ("GetDiskType: Couldn't lock partition", 0L);
  281.     rc = ERR_NO_PAGING_FILE;
  282.     }
  283.   FreeMem (InfoData, sizeof (struct InfoData));
  284.   }
  285. else
  286.   rc = ERR_NOT_ENOUGH_MEM;
  287.  
  288. return (rc);
  289. }
  290.  
  291. /*****************************************************************/
  292.  
  293. int IsValidFFSPartition (void)
  294.  
  295. /* name is the name of a file on the disk to be checked. */
  296. {
  297. int rc;
  298.  
  299. if (PagingDevParams.block_size != BLOCKSIZE || PagingDevParams.secs_per_block != 1)
  300.   return (ERR_WRONG_BLOCKSIZE);
  301.  
  302. if ((rc = GetDiskType (PartWithColon, &DiskType)) == SUCCESS)
  303.   {
  304.   if (DiskType != ID_FFS_DISK && DiskType != ID_INTER_FFS_DISK && 
  305.       DiskType != ID_FASTDIR_FFS_DISK)
  306.     rc = ERR_NO_FFS;
  307.   }
  308.  
  309. return (rc);
  310. }
  311.  
  312. /*****************************************************************/
  313.  
  314. PRIVATE int RWBitmap (ULONG RootNum, BOOL DoRead)
  315.  
  316. {
  317. struct FFSBitmapExtBlock *BMExtBlock;
  318. int i, j;
  319. ULONG *current_data;
  320. ULONG ExtBlock;
  321. int retries = 0;
  322. LONG sum;
  323.  
  324. PRINT_DEB ("RWBitmap called", 0L);
  325.  
  326. current_data = Bitmap;
  327.  
  328. BMExtBlock = (struct FFSBitmapExtBlock*) RootBlock;
  329.  
  330. PRINT_DEB ("RWBitmap: Reading rootblock from %ld", RootNum);
  331.  
  332. /* Wait for the bitmap to become valid */
  333. while (ReadBlock (RootNum, RootBlock),
  334.        !RootBlock->BitmapValid && retries++ < MAX_RETRIES)
  335.   Delay (WAITING_TIME);
  336.  
  337. if (!RootBlock->BitmapValid)
  338.   {
  339.   PRINT_DEB ("RWBitmap: Bitmap is invalid", 0L);
  340.   return (ERR_NOT_VALIDATED);
  341.   }
  342.  
  343. for (i = 0; i < 25; i++)
  344.   {
  345.   if (RootBlock->BitmapBlocks [i] == 0)
  346.     return (SUCCESS);
  347.   else
  348.     {
  349.     PRINT_DEB ("RWBitmap: Transferring bitmapblock %ld", 
  350.                RootBlock->BitmapBlocks [i]);
  351.     if (DoRead)
  352.       ReadBlock (RootBlock->BitmapBlocks [i], current_data);
  353.     else
  354.       {
  355.       sum = 0;
  356.       for (j = 1; j < BLOCKSIZE / 4; j++)
  357.         sum += *(current_data + j);
  358.       *current_data = -sum;
  359.       WriteBlock (RootBlock->BitmapBlocks [i], current_data);
  360.       }
  361.     current_data += BLOCKSIZE / sizeof (ULONG);
  362.     }
  363.   }
  364.  
  365. ExtBlock = RootBlock->BitmapExtBlock;
  366.  
  367. while (ExtBlock != 0)
  368.   {
  369.   PRINT_DEB ("RWBitmap: Reading BitmapExtBlock %ld", ExtBlock);
  370.   ReadBlock (ExtBlock, BMExtBlock);  
  371.   for (i = 0; i < BLOCKSIZE/4 - 1; i++)
  372.     {
  373.     if (BMExtBlock->BitmapBlocks [i] == 0)
  374.       return (SUCCESS);
  375.     else
  376.       {
  377.       PRINT_DEB ("ReadBitmap: Transferring bitmapblock %ld", 
  378.                  BMExtBlock->BitmapBlocks [i]);
  379.       if (DoRead)
  380.         ReadBlock (BMExtBlock->BitmapBlocks [i], current_data);
  381.       else
  382.         {
  383.         sum = 0;
  384.         for (j = 1; j < BLOCKSIZE / 4; j++)
  385.           sum += *(current_data + j);
  386.         *current_data = -sum;
  387.         WriteBlock (BMExtBlock->BitmapBlocks [i], current_data);
  388.         }
  389.       current_data += BLOCKSIZE / sizeof (ULONG);
  390.       }
  391.     }
  392.   ExtBlock = BMExtBlock->NextBitmapExtBlock;
  393.   }
  394.  
  395. return (SUCCESS);
  396. }
  397.  
  398. /*****************************************************************/
  399.  
  400. PRIVATE ULONG AllocBlock (void)
  401.  
  402. {
  403. ULONG i;
  404. static ULONG last_found = 0;
  405.  
  406. for (i = last_found + 1;
  407.      i < PagingDevParams.res_start + NumBlocks; i++)
  408.   {
  409.   if (!BlockUsed (i))
  410.     {
  411.     AllocThisBlock (i);
  412.     last_found = i;
  413.     return (i);
  414.     }
  415.   }
  416. return (0L);
  417. }
  418.  
  419. /*****************************************************************/
  420.  
  421. PRIVATE ULONG FindContiguousChunk (ULONG FileBlocks, ULONG *Largest)
  422.  
  423. /* Tries to find a contiguous chunk of 'FileBlocks' blocks. If it
  424.  * doesn't find such, it returns the size of the largest available
  425.  * chunk.
  426.  */
  427. {
  428. ULONG last_used = PagingDevParams.res_start - 1;
  429. ULONG i;
  430. ULONG start;
  431.  
  432. PRINT_DEB ("FindContiguousChunk (%ld)", FileBlocks);
  433.  
  434. *Largest = 0;
  435.  
  436. for (i = PagingDevParams.res_start; 
  437.      i < PagingDevParams.res_start + NumBlocks; i++)
  438.   {
  439.   if (BlockUsed (i))
  440.     last_used = i;
  441.   else
  442.     {
  443.     if (i >= last_used + FileBlocks)
  444.       {
  445.       start = last_used + 1;
  446.       for (i = start; i < start + FileBlocks; i++)
  447.         AllocThisBlock (i);
  448.       return (start);
  449.       }
  450.     else if (i - last_used > *Largest)
  451.       *Largest = i - last_used;
  452.     }
  453.   }
  454. return (0L);
  455. }
  456.  
  457. /*****************************************************************/
  458.  
  459. PRIVATE void UpdateDCFFSCache (ULONG file_block, ULONG dir_block,
  460.                                ULONG filesize)
  461.  
  462. {
  463. struct FFSDirBlock *dir;
  464. struct DCFFSCacheBlock *cache;
  465. struct ListEntry *le;
  466. int i;
  467. ULONG cur_block;
  468. char *tmp;
  469.  
  470. ReadBlock (dir_block, MiscBlock);
  471. dir = (struct FFSDirBlock*) MiscBlock;
  472. cache = (struct DCFFSCacheBlock*) MiscBlock;
  473. cur_block = dir->DirList;
  474.  
  475. do
  476.   {
  477.   ReadBlock (cur_block, MiscBlock);
  478.   PRINT_DEB ("Reading cache block. NumEntries = %ld", cache->NumEntries);
  479.   le = (struct ListEntry*) (cache + 1);
  480.   if (cache->Type != T_DIRLIST)
  481.     {
  482.     PRINT_DEB ("UpdateDCFFSCache: Unknown cache block format", 0L);
  483.     return;
  484.     }
  485.  
  486.   for (i = 0; i < cache->NumEntries; i++)
  487.     {
  488.     if (le->Key == file_block)
  489.       {
  490.       PRINT_DEB ("UpdateDCFFSCache: Found file", 0L);
  491.       PRINT_DEB ("UpdateDCFFSCache: Entering size %ld in cache block", filesize);
  492.       le->Size = filesize;
  493.       Checksum (MiscBlock);
  494.       WriteBlock (cur_block, MiscBlock);
  495.       return;
  496.       }
  497.  
  498.     tmp = &(le->FileNameLength);
  499.     PRINT_DEB ("UpdateDCFFSCache: Current file name is", 0L);
  500.     PRINT_DEB (tmp + 1, 0L);
  501.     tmp += *tmp + 1;            /* Skip name (BCPL string) */
  502.     tmp += *tmp + 1;            /* Skip comment (BCPL string) */
  503.     if ((ULONG)tmp & 1)
  504.       tmp++;                    /* word align */
  505.     le = (struct ListEntry *) tmp;
  506.     }
  507.   } while ((cur_block = cache->NextBlock) != NULL);
  508. }
  509.  
  510. /*****************************************************************/
  511.  
  512. int CreatePseudoPart (ULONG header_block, ULONG *first_block, 
  513.                       ULONG *last_block)
  514.  
  515. {
  516. ULONG Root;
  517. ULONG Used = 0;
  518. #ifdef DEBUG
  519. ULONG last_used,
  520.       last_unused;
  521. #endif
  522. int i;
  523. ULONG BlocksForFile;
  524. ULONG NumDataBlocks;
  525. ULONG FileStart;
  526. struct FFSFileHeader *fh;
  527. struct FFSExtBlock *eb;
  528. ULONG ExtBlockNum;
  529. ULONG CurrentBlock;
  530. ULONG ParentBlock;
  531. ULONG BitmapBlocks;
  532. ULONG LargestChunk;
  533. ULONG FreeBlocks;
  534. ULONG FileSize;
  535. int rc;
  536.  
  537. PRINT_DEB ("CreatePseudoPart called", 0L);
  538.  
  539. NumBlocks = (PagingDevParams.high_cyl - PagingDevParams.low_cyl + 1) * 
  540.              PagingDevParams.heads * PagingDevParams.secs_per_track;
  541. PRINT_DEB ("NumBlocks = %ld", NumBlocks);
  542.  
  543. Root = (NumBlocks + 1) / 2;
  544.  
  545. NumBlocks -= PagingDevParams.res_start + PagingDevParams.res_end;
  546. BitmapBlocks = (NumBlocks + (BITS_PER_BMBLOCK - 1)) / BITS_PER_BMBLOCK;
  547.  
  548. if ((rc = InitFFS (BitmapBlocks)) != SUCCESS)
  549.   return (rc);
  550.  
  551. if ((rc = RWBitmap (Root, DO_READ)) != SUCCESS)
  552.   {
  553.   PRINT_DEB ("CreatePseudoPart: Couldn't read bitmap", 0L);
  554.   Cleanup ();
  555.   return (rc);
  556.   }
  557.  
  558. #ifdef DEBUG
  559. last_used = PagingDevParams.res_start - 1;
  560. last_unused = 0;
  561. #endif
  562.  
  563. for (i = PagingDevParams.res_start; 
  564.      i < PagingDevParams.res_start + NumBlocks; i++)
  565.   {
  566.   if (BlockUsed ((ULONG)i))
  567.     {
  568. #ifdef DEBUG
  569.     if (last_unused == i - 1)
  570.       PRINT_DEB ("Used block starts at %ld", (ULONG)i);
  571.     last_used = i;
  572. #endif
  573.     Used++;
  574.     }
  575. #ifdef DEBUG
  576.   else
  577.     {
  578.     if (last_used == i - 1)
  579.       PRINT_DEB ("Unused block starts at %ld", (ULONG)i);
  580.     last_unused = i;
  581.     }
  582. #endif
  583.   }
  584.  
  585. PRINT_DEB ("NumUsed = %ld", Used);
  586.  
  587. NumDataBlocks = (CurrentConfig.FileSize * 1024L * 1024L + 
  588.                  BLOCKSIZE - 1) / BLOCKSIZE;
  589. /* 72 pointers to blocks are in the fileheader. In each file extension 
  590.  * block there are 72 pointers to additional blocks.
  591.  */
  592.  
  593. BlocksForFile = NumDataBlocks + (NumDataBlocks + 71) / 72;
  594. FreeBlocks = NumBlocks - Used;
  595.  
  596. if (BlocksForFile > FreeBlocks)
  597.   {
  598.   char error_msg [200];
  599.  
  600.   PRINT_DEB ("CreatePseudoPart: Not enough space on device left", 0L);
  601.   sprintf (error_msg, GetVMMString (msgPPTooLarge),
  602.            (FreeBlocks - (FreeBlocks + 71) / 72) / 2 / 1024);
  603.   ReportError (error_msg, ERR_CONTINUE);
  604.   Cleanup ();
  605.   return (ERR_MSG_POSTED);
  606.   }
  607.  
  608. if ((FileStart = FindContiguousChunk (NumDataBlocks, &LargestChunk)) == NULL)
  609.   {
  610.   char error_msg [200];
  611.  
  612.   PRINT_DEB ("CreatePseudoPart: Couldn't find large enough contiguous chunk", 0L);
  613.   sprintf (error_msg, GetVMMString (msgTooFragmented), LargestChunk / 2048);
  614.   ReportError (error_msg, ERR_CONTINUE);
  615.   Cleanup ();
  616.   return (ERR_MSG_POSTED);
  617.   }
  618.  
  619. PRINT_DEB ("CreatePseudoPart: Found contiguous chunk starting at %ld", FileStart);
  620.  
  621. *first_block = FileStart;
  622. *last_block  = FileStart + NumDataBlocks - 1;
  623.  
  624. fh = (struct FFSFileHeader*) MiscBlock;
  625. eb = (struct FFSExtBlock*)MiscBlock;
  626.  
  627. ReadBlock (header_block, fh);
  628. fh->FirstDataBlock = FileStart;
  629. fh->Size = FileSize = NumDataBlocks * BLOCKSIZE;
  630. fh->Type = T_SHORT;
  631. fh->SecondaryType = ST_FILE;
  632. fh->OwnKey = header_block;
  633. CurrentBlock = header_block;
  634. ParentBlock = fh->ParentKey;
  635.  
  636. do
  637.   {
  638.   eb->BlocksInTable = MIN (72, NumDataBlocks);
  639.   for (i = 0; i < eb->BlocksInTable; i++)
  640.     eb->DataBlocks [71-i] = FileStart++;
  641.  
  642.   NumDataBlocks -= eb->BlocksInTable;
  643.   if (NumDataBlocks > 0)
  644.     {
  645.     ExtBlockNum = AllocBlock ();
  646.     eb->ExtBlock = ExtBlockNum;
  647.     }
  648.   else
  649.     {
  650.     eb->ExtBlock = 0L;
  651.     for (i = eb->BlocksInTable; i < 72; i++)
  652.       eb->DataBlocks [71-i] = 0L;
  653.     }
  654.  
  655.   Checksum (eb);
  656.   WriteBlock (CurrentBlock, eb);
  657.  
  658.   eb->Type = T_LIST;
  659.   eb->SecondaryType = ST_FILE;
  660.   CurrentBlock = ExtBlockNum;
  661.   eb->OwnKey = CurrentBlock;
  662.   eb->HeaderKey = header_block;
  663.   }
  664. while (eb->ExtBlock != 0L);
  665.  
  666. if ((rc = RWBitmap (Root, DO_WRITE)) != SUCCESS)
  667.   {
  668.   PRINT_DEB ("Couldn't write modified bitmap", 0L);
  669.   ReadBlock (Root, RootBlock);
  670.   RootBlock->BitmapValid = FALSE;       /* Let filesystem structure validate */
  671.   Checksum (RootBlock);
  672.   WriteBlock (Root, RootBlock);
  673.   }
  674.  
  675. if (DiskType == ID_FASTDIR_FFS_DISK)
  676.   UpdateDCFFSCache (header_block, ParentBlock, FileSize);
  677.  
  678. Cleanup ();
  679. return (SUCCESS);
  680. }
  681.